#pragma

#include <memory>
#include <vector>
#include <cstdint>
#include <iostream>
#include <unistd.h>
#include <functional>
/**
 * 时间轮定时器设计思想
 *      1. 设计二维数组来存储相同时间的定时器任务
 *      2. 使用智能指针管理任务，管理连接，保证每个连接时在存活时间结束后能够释放，而不是累计存活时间达到后就直接释放连接
 *      3. 如果一个链接始终有请求，那么每次请求后都需要把定时器重置，这样才能保证定时器的正确性
 */

using TaskFunc = std::function<void()>;
using ReleaseFunc = std::function<void()>;
constexpr int capa = 60;
class TimerTask
{
public:
    TimerTask(uint64_t id, uint32_t delay, const TaskFunc &cb)
        : _id(id),
          _timeout(delay),
          _iscancel(false),
          _task_cb(cb)
    {
    }
    void SetCancle() { _iscancel = true; }
    bool getCancle() { return _iscancel; }
    void SetRelease(ReleaseFunc rel) { _relfunc = rel; }
    ~TimerTask()
    {
        // 定时器结束后就需要执行任务和释放连接
        // 定时器被取消说明这个任务不需要执行了，没被取消才说明是时间到了，该执行了
        if (_iscancel == false)
            _task_cb();
        _relfunc();
    }

    uint32_t getTimeOut() { return _timeout; }

private:
    uint64_t _id;         // 定时器id
    uint32_t _timeout;    // 超时时间
    bool _iscancel;       // 是否被取消，false否，true是
    TaskFunc _task_cb;    // 定时器结束后要回调执行的定时任务，
    ReleaseFunc _relfunc; // 释放任务的回调函数
};

class TimeQueue
{
    using PtrTask = std::shared_ptr<TimerTask>;
    using WeakTask = std::weak_ptr<TimerTask>;

private:
    void TimerRemove(uint64_t id)
    {
        auto it = _timer.find(id);
        if (it != _timer.end())
            _timer.erase(id);
    }

public:
    TimeQueue()
        : _tick(0),
          _wheel(_capacity)
    {
    }

    void TimerAdd(uint64_t id, uint32_t delay, const TaskFunc &tf)
    {
        PtrTask pt(new TimerTask(id, delay, tf));
        pt->SetRelease(std::bind(&TimeQueue::TimerRemove, this, id));
        int pos = (_tick + delay) % _capacity;
        _wheel[pos].push_back(pt);
        _timer[id] = WeakTask(pt);
    }

    void TimerRefersh(uint64_t id)
    {
        auto it = _timer.find(id);
        if (it == _timer.end())
            return;

        // 获取weakptr指向的shared_ptr资源
        PtrTask pt = it->second.lock();
        if (!pt)
            return;

        // 先前的任务和接下来要放进去的任务要做的任务都是一样的，要把前面的删除了才可以
        // 否则vector里存在两个相同的任务，但是前面的没删除，刷新没起到作用
        _timer.erase(id);

        int pos = (_tick + pt->getTimeOut()) % _capacity;
        _wheel[pos].push_back(pt);
    }

    void TimerCancel(uint64_t id)
    {
        auto it = _timer.find(id);
        if (it == _timer.end())
            return;

        PtrTask pt = it->second.lock();
        if (pt)
            pt->SetCancle();
    }

    void RunTimerTask()
    {
        _tick = (_tick + 1) % _capacity;
        _wheel[_tick].clear();
    }

private:
    int _tick;            // 要执行的任务的指针，指向谁，谁就要执行
    int _capacity = capa; // 一个时间轮的最大单位，这里以秒为单位，最大为60
    // 用一个二维数组管理定时器
    std::vector<std::vector<PtrTask>> _wheel;
    // 使用弱指针管理，防止shared_ptr的循环引用问题
    std::unordered_map<uint64_t, WeakTask> _timer;
};

class Test
{
public:
    Test()
    {
        std::cout << "构造" << std::endl;
    }
    ~Test() { std::cout << "析构" << std::endl; }
};

void DelTest(Test *t)
{
    delete t;
}

int main()
{
    TimeQueue tw;

    Test *t = new Test();

    tw.TimerAdd(888, 5, std::bind(DelTest, t));

    for (int i = 0; i < 5; i++)
    {
        sleep(1);
        tw.TimerRefersh(888); // 刷新定时任务
        tw.RunTimerTask();    // 向后移动秒针
        std::cout << "刷新了一下定时任务，重新需要5s中后才会销毁\n";
    }
    tw.TimerCancel(888);
    while (1)
    {
        sleep(1);
        std::cout << "-------------------\n";
        tw.RunTimerTask(); // 向后移动秒针
    }
    return 0;
}